export function webRtcPlayer(parOptions) {
  parOptions = parOptions || {};

  var self = this;

  //**********************
  //Config setup
  //**********************;
  this.cfg = parOptions.peerConnectionOptions || {};
  this.cfg.sdpSemantics = "unified-plan";
  this.pcClient = null;
  this.dcClient = null;
  this.tnClient = null;

  this.sdpConstraints = {
    offerToReceiveAudio: 1,
    offerToReceiveVideo: 1,
  };

  // See https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit for values
  this.dataChannelOptions = { ordered: true };

  //**********************
  //Functions
  //**********************

  //Create Video element and expose that as a parameter
  let createWebRtcVideo = function () {
    // var video = document.createElement("video");
    // video.id = "streamingVideo";
    // video.style = "width:100%; height:100%";
    // video.disablepictureinpicture = true;
    // video.playsInline = true;
    var video = document.getElementById("streamingVideo");
    video.muted = "muted";
    video.disablepictureinpicture = true;
    // video.object-fit = 'fill';
    video.addEventListener(
      "loadedmetadata",
      function () {
        if (self.onVideoInitialised) {
          self.onVideoInitialised();
        }
      },
      true
    );
    return video;
  };

  this.video = createWebRtcVideo();

  this.cfg.offerExtmapAllowMixed = false;

  let onsignalingstatechange = function (state) {
    console.info("🍺signaling state change:", state);
  };

  let oniceconnectionstatechange = function (state) {
    console.info("🍺🍺ice connection state change:", state);
  };

  let onicegatheringstatechange = function (state) {
    console.info("🍺🍺🍺ice gathering state change:", state);
  };

  let handleOnTrack = function (e) {
    console.info("🍺🍺🍺🍺🍺🍺handleOnTrack:", e);
    // console.log("🚀 🚀 🚀 🚀 🚀 🚀 e", e);
    console.log(
      "🚀 🚀 💗🚀 🚀 🚀 🚀 self.video.srcObject ",
      self.video,
      self.video.srcObject
    );
    {
      self.video.srcObject = e.streams[0];
      console.log("Set video source from video track ontrack.");
      return;
    }
    if (self.video.srcObject !== e.streams[0]) {
      console.log("setting video stream from ontrack");
      self.video.srcObject = e.streams[0];
    }
  };

  let setupDataChannel = function (pc, label, options) {
    try {
      var datachannel = pc.createDataChannel(label, options);
      console.log(`Created datachannel (${label})`);

      datachannel.onopen = function () {
        console.log(`data channel (${label}) connect`);
        if (self.onDataChannelConnected) {
          self.onDataChannelConnected();
        }
      };

      datachannel.onclose = function () {
        console.log(`data channel (${label}) closed`);
      };

      datachannel.onmessage = function (e) {
        //console.log(`Got message (${label})`, e.data)
        if (self.onDataChannelMessage) self.onDataChannelMessage(e.data);
      };

      return datachannel;
    } catch (e) {
      console.warn("No data channel", e);
      return null;
    }
  };

  let onicecandidate = function (e) {
    console.log("🍦ICE candidate", e);
    if (e.candidate && e.candidate.candidate) {
      self.onWebRtcCandidate(e.candidate);
    }
  };

  let handleCreateOffer = function (pc) {
    console.log("🔥 handleCreateOffer");
    pc.createOffer(self.sdpConstraints).then(
      function (offer) {
        pc.setLocalDescription(offer);
        if (self.onWebRtcOffer) {
          // (andriy): increase start bitrate from 300 kbps to 20 mbps and max bitrate from 2.5 mbps to 100 mbps
          // (100 mbps means we don't restrict encoder at all)
          // after we `setLocalDescription` because other browsers are not c happy to see google-specific config
          offer.sdp = offer.sdp.replace(
            /(a=fmtp:\d+ .*level-asymmetry-allowed=.*)\r\n/gm,
            "$1;x-google-start-bitrate=10000;x-google-max-bitrate=20000\r\n"
          );
          self.onWebRtcOffer(offer);
        }
      },
      function () {
        console.warn("Couldn't create offer");
      }
    );
  };

  let setupPeerConnection = function (pc) {
    if (pc.SetBitrate)
      console.log("Hurray! there's RTCPeerConnection.SetBitrate function");

    //Setup peerConnection events
    pc.onsignalingstatechange = onsignalingstatechange;
    pc.oniceconnectionstatechange = oniceconnectionstatechange;
    pc.onicegatheringstatechange = onicegatheringstatechange;

    pc.ontrack = handleOnTrack;
    pc.onicecandidate = onicecandidate;
    console.log("🍉 pc", pc);
  };

  let generateAggregatedStatsFunction = function () {
    if (!self.aggregatedStats) self.aggregatedStats = {};

    return function (stats) {
      //console.log('Printing Stats');

      let newStat = {};
      //console.log('----------------------------- Stats start -----------------------------');
      stats.forEach((stat) => {
        //                    console.log(JSON.stringify(stat, undefined, 4));
        if (
          stat.type == "inbound-rtp" &&
          !stat.isRemote &&
          (stat.mediaType == "video" || stat.id.toLowerCase().includes("video"))
        ) {
          newStat.timestamp = stat.timestamp;
          newStat.bytesReceived = stat.bytesReceived;
          newStat.framesDecoded = stat.framesDecoded;
          newStat.packetsLost = stat.packetsLost;
          newStat.bytesReceivedStart =
            self.aggregatedStats && self.aggregatedStats.bytesReceivedStart
              ? self.aggregatedStats.bytesReceivedStart
              : stat.bytesReceived;
          newStat.framesDecodedStart =
            self.aggregatedStats && self.aggregatedStats.framesDecodedStart
              ? self.aggregatedStats.framesDecodedStart
              : stat.framesDecoded;
          newStat.timestampStart =
            self.aggregatedStats && self.aggregatedStats.timestampStart
              ? self.aggregatedStats.timestampStart
              : stat.timestamp;

          if (self.aggregatedStats && self.aggregatedStats.timestamp) {
            if (self.aggregatedStats.bytesReceived) {
              // bitrate = bits received since last time / number of ms since last time
              //This is automatically in kbits (where k=1000) since time is in ms and stat we want is in seconds (so a '* 1000' then a '/ 1000' would negate each other)
              newStat.bitrate =
                (8 *
                  (newStat.bytesReceived -
                    self.aggregatedStats.bytesReceived)) /
                (newStat.timestamp - self.aggregatedStats.timestamp);
              newStat.bitrate = Math.floor(newStat.bitrate);
              newStat.lowBitrate =
                self.aggregatedStats.lowBitrate &&
                self.aggregatedStats.lowBitrate < newStat.bitrate
                  ? self.aggregatedStats.lowBitrate
                  : newStat.bitrate;
              newStat.highBitrate =
                self.aggregatedStats.highBitrate &&
                self.aggregatedStats.highBitrate > newStat.bitrate
                  ? self.aggregatedStats.highBitrate
                  : newStat.bitrate;
            }

            if (self.aggregatedStats.bytesReceivedStart) {
              newStat.avgBitrate =
                (8 *
                  (newStat.bytesReceived -
                    self.aggregatedStats.bytesReceivedStart)) /
                (newStat.timestamp - self.aggregatedStats.timestampStart);
              newStat.avgBitrate = Math.floor(newStat.avgBitrate);
            }

            if (self.aggregatedStats.framesDecoded) {
              // framerate = frames decoded since last time / number of seconds since last time
              newStat.framerate =
                (newStat.framesDecoded - self.aggregatedStats.framesDecoded) /
                ((newStat.timestamp - self.aggregatedStats.timestamp) / 1000);
              newStat.framerate = Math.floor(newStat.framerate);
              newStat.lowFramerate =
                self.aggregatedStats.lowFramerate &&
                self.aggregatedStats.lowFramerate < newStat.framerate
                  ? self.aggregatedStats.lowFramerate
                  : newStat.framerate;
              newStat.highFramerate =
                self.aggregatedStats.highFramerate &&
                self.aggregatedStats.highFramerate > newStat.framerate
                  ? self.aggregatedStats.highFramerate
                  : newStat.framerate;
            }

            if (self.aggregatedStats.framesDecodedStart) {
              newStat.avgframerate =
                (newStat.framesDecoded -
                  self.aggregatedStats.framesDecodedStart) /
                ((newStat.timestamp - self.aggregatedStats.timestampStart) /
                  1000);
              newStat.avgframerate = Math.floor(newStat.avgframerate);
            }
          }
        }

        //Read video track stats
        if (
          stat.type == "track" &&
          (stat.trackIdentifier == "video_label" || stat.kind == "video")
        ) {
          newStat.framesDropped = stat.framesDropped;
          newStat.framesReceived = stat.framesReceived;
          newStat.framesDroppedPercentage =
            (stat.framesDropped / stat.framesReceived) * 100;
          newStat.frameHeight = stat.frameHeight;
          newStat.frameWidth = stat.frameWidth;
          newStat.frameHeightStart =
            self.aggregatedStats && self.aggregatedStats.frameHeightStart
              ? self.aggregatedStats.frameHeightStart
              : stat.frameHeight;
          newStat.frameWidthStart =
            self.aggregatedStats && self.aggregatedStats.frameWidthStart
              ? self.aggregatedStats.frameWidthStart
              : stat.frameWidth;
        }

        if (
          stat.type == "candidate-pair" &&
          stat.currentRoundTripTime !== undefined &&
          stat.currentRoundTripTime != 0
        ) {
          newStat.currentRoundTripTime = stat.currentRoundTripTime;
        }
      });

      //console.log(JSON.stringify(newStat));
      self.aggregatedStats = newStat;

      if (self.onAggregatedStats) self.onAggregatedStats(newStat);
    };
  };

  // let setupTracksToSendAsync = async function (pc) {
  //   // Setup a transceiver for getting UE video
  //   pc.addTransceiver("video", { direction: "recvonly" });

  //   // Setup a transceiver for sending mic audio to UE and receiving audio from UE
  //   if (!self.useMic) {
  //     pc.addTransceiver("audio", { direction: "recvonly" });
  //   } else {
  //     let audioSendOptions = self.useMic
  //       ? {
  //           autoGainControl: false,
  //           channelCount: 1,
  //           echoCancellation: false,
  //           latency: 0,
  //           noiseSuppression: false,
  //           sampleRate: 16000,
  //           volume: 1.0,
  //         }
  //       : false;

  //     // Note using mic on android chrome requires SSL or chrome://flags/ "unsafely-treat-insecure-origin-as-secure"
  //     const stream = await navigator.mediaDevices.getUserMedia({
  //       video: false,
  //       audio: audioSendOptions,
  //     });
  //     if (stream) {
  //       for (const track of stream.getTracks()) {
  //         if (track.kind && track.kind == "audio") {
  //           pc.addTransceiver(track, { direction: "sendrecv" });
  //         }
  //       }
  //     } else {
  //       pc.addTransceiver("audio", { direction: "recvonly" });
  //     }
  //   }
  // };

  //**********************
  //Public functions
  //**********************

  //This is called when revceiving new ice candidates individually instead of part of the offer
  //This is currently not used but would be called externally from this class
  this.handleCandidateFromServer = function (iceCandidate) {
    console.log("ICE candidate: ", iceCandidate);
    let candidate = new RTCIceCandidate(iceCandidate);
    self.pcClient.addIceCandidate(candidate).then(() => {
      console.log("ICE candidate successfully added");
    });
  };

  //Called externaly to create an offer for the server
  this.createOffer = function () {
    if (self.pcClient) {
      console.log("Closing existing PeerConnection");
      self.pcClient.close();
      self.pcClient = null;
    }
    self.pcClient = new RTCPeerConnection(self.cfg);
    // setupTracksToSendAsync(self.pcClient).finally(function () {
    //   setupPeerConnection(self.pcClient);
    //   self.dcClient = setupDataChannel(
    //     self.pcClient,
    //     "cirrus",
    //     self.dataChannelOptions
    //   );
    //   handleCreateOffer(self.pcClient);
    // });
    console.log("🔥 setupPeerConnection调用");
    setupPeerConnection(self.pcClient);
    self.dcClient = setupDataChannel(
      self.pcClient,
      "cirrus",
      self.dataChannelOptions
    );
    handleCreateOffer(self.pcClient);
  };

  //Called externaly when an answer is received from the server
  this.receiveAnswer = function (answer) {
    console.log(`Received answer:\n${answer}`);
    var answerDesc = new RTCSessionDescription(answer);
    self.pcClient.setRemoteDescription(answerDesc);
  };

  this.close = function () {
    if (self.pcClient) {
      console.log("Closing existing peerClient");
      self.pcClient.close();
      self.pcClient = null;
    }
    if (self.aggregateStatsIntervalId)
      clearInterval(self.aggregateStatsIntervalId);
  };

  //Sends data across the datachannel
  this.send = function (data) {
    if (self.dcClient && self.dcClient.readyState == "open") {
      // console.log('Sending data on dataconnection', self.dcClient)
      self.dcClient.send(data);
    }
  };

  this.getStats = function (onStats) {
    if (self.pcClient && onStats) {
      self.pcClient.getStats(null).then((stats) => {
        onStats(stats);
      });
    }
  };

  this.aggregateStats = function (checkInterval) {
    let calcAggregatedStats = generateAggregatedStatsFunction();
    let printAggregatedStats = () => {
      self.getStats(calcAggregatedStats);
    };
    self.aggregateStatsIntervalId = setInterval(
      printAggregatedStats,
      checkInterval
    );
  };
}
